package org.dromara.system.kernel;

import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.JSONWriter;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.system.domain.PayAccount;
import org.dromara.system.domain.PayOrder;
import org.dromara.system.domain.PayOrderLog;
import org.dromara.system.domain.bo.aparams.AOrderBo;
import org.dromara.system.domain.bo.config.CustomField;
import org.dromara.system.domain.bo.config.LogisticsConfig;
import org.dromara.system.domain.mq.OrderTrackParam;
import org.dromara.system.domain.vo.PayOrderTrackVo;
import org.dromara.system.domain.vo.PayTypeVo;
import org.dromara.system.domain.vo.TrackInfoVo;
import org.dromara.system.mapper.PayAccountMapper;
import org.dromara.system.mapper.PayTypeMapper;
import org.dromara.system.mq.OrderTrackSender;
import org.dromara.system.service.IPayAccountService;
import org.dromara.system.service.IPayOrderLogService;
import org.dromara.system.service.IPayOrderService;
import org.dromara.system.utils.RequestUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.elasticsearch.index.query.TermsQueryBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.PageImpl;
import org.springframework.stereotype.Service;

import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

/**
 * 物流推送核心处理
 */
@Service
@RequiredArgsConstructor
public class TrackPushKernel {

    private static final Logger log = LoggerFactory.getLogger(TrackPushKernel.class);
    private final OrderSearchKernel orderSearchKernel;
    private final PayTypeMapper payTypeMapper;
    private final PayAccountMapper accountMapper;
    private final IPayOrderService orderService;
    private final OrderTrackSender trackSender;
    private final IPayAccountService accountService;
    private final IPayOrderLogService orderLogService;

    /**
     * 物流推送
     *
     * @param list
     * @return 结果
     */
    public R<?> push(List<JSONObject> list) {

        if (list == null || list.isEmpty()) {
            return R.fail("未读取到数据");
        }
        List<JSONObject> noneList = list.stream().filter(tmp -> StringUtils.isEmpty(tmp.getString("orderNo"))).toList();
        if (!noneList.isEmpty()) {
            return R.fail("检测到" + noneList.size() + "数据订单号为空，请检查");
        }
        Set<String> orderNos = list.stream().map(tmp -> tmp.getString("orderNo")).collect(Collectors.toSet());
        if (orderNos.size() < list.size()) {
            return R.fail("检测到" + (list.size() - orderNos.size()) + "数据订单号重复，请检查");
        }
//        Set<String> trackNos = list.stream().map(tmp -> tmp.getString("tracking_number")).collect(Collectors.toSet());
//        if (trackNos.size() < list.size()) {
//            return R.fail("检测到" + (list.size() - trackNos.size()) + "数据物流单号重复，请检查");
//        }

        //构建查询条件
        BoolQueryBuilder queryBuilder = new BoolQueryBuilder();
        queryBuilder.filter(new TermsQueryBuilder("orderNo.keyword", orderNos));
        if (LoginHelper.isAgent()) {
            queryBuilder.filter(new TermQueryBuilder("agentId", LoginHelper.getUserId()));
        }
        if (LoginHelper.isClient()) {
            queryBuilder.filter(new TermQueryBuilder("clientId", LoginHelper.getUserId()));
        }
        PageImpl<PayOrderTrackVo> page = orderSearchKernel.queryOrderEsPage(queryBuilder, null, null, PayOrderTrackVo.class, null, Arrays.stream(PayOrderTrackVo.class.getDeclaredFields()).map(Field::getName).toArray(String[]::new));
        List<PayOrderTrackVo> orderList = page.getContent();

        //校验在系统中不存在的订单号，返回提示
        if (orderList.size() < orderNos.size()) {
            Set<String> orderNos2 = orderList.stream().map(PayOrderTrackVo::getOrderNo).collect(Collectors.toSet());
            Set<String> notExistOrderNos = orderNos.stream().filter(tmp -> !orderNos2.contains(tmp)).collect(Collectors.toSet());
            return R.fail("以下订单号在系统中不存在，请检查\n" + (String.join(",", notExistOrderNos)));
        }

        //获取所以平台的物流配置
        Set<Long> typeIds = orderList.stream().map(PayOrderTrackVo::getPayTypeId).collect(Collectors.toSet());
        Map<Long, LogisticsConfig> logisMap = getLogisticsConfig(typeIds);

        //把传入的参数list以orderNo为key, value为单个对象的map
        Map<String, JSONObject> map = list.stream().collect(Collectors.toMap(tmp -> tmp.getString("orderNo"), tmp -> tmp));

        //构建更新对象
        List<PayOrder> updates = new ArrayList<>();
        orderList.forEach(order -> {
            JSONObject json = map.get(order.getOrderNo());
            if (json == null) {
                return;
            }

            //获取物流字段配置
            LogisticsConfig config = logisMap.get(order.getPayTypeId());
            if (config == null || !config.getShow()) {
                return;
            }

            //构建物流信息对象
            TrackInfoVo track = new TrackInfoVo();
            track.setOrder_id(order.getOrderId());
            track.setSession_id(order.getSessionId());
            track.setOrder_cid(order.getOrderCid());
            track.setTerminal_order_sn(order.getOrderSn());
            track.setCharge_id(order.getChargeId());
            //物流字段
            track.setTrackers(buildTrackField(json, config.getLogistItem()));
            //地址字段
            track.setAddress(buildAddressField(order.getCustomer(), json, config.getAddressItem()));

            //构建更新对象
            PayOrder up = new PayOrder();
            up.setOrderId(order.getOrderId());
            up.setTrackStatus(1);
            up.setTrackNo(json.getString("tracking_number"));
            up.setTrackInfo(JSONObject.toJSONString(track, JSONWriter.Feature.WriteMapNullValue));
            updates.add(up);
        });

        //更新物流信息到订单中
        if (!updates.isEmpty()) {
            orderService.updatePayOrderBatch(updates);
        }

        CompletableFuture.supplyAsync(() -> {
            for (PayOrder up : updates) {
                trackSender.sendTrackMessage(new OrderTrackParam(up.getOrderId(), 1), 1000L);
            }
            return Boolean.TRUE;
        });

        return R.ok("提交任务成功，请稍后查看结果");
    }


    /**
     * 处理推送
     *
     * @param param 推送参数
     */
    public void handlePush(OrderTrackParam param) {
        PayOrder order = orderService.selectById(param.getOrderId());
        if (order == null) {
            return;
        }

        //记录请求日志
        PayOrderLog log = orderLogService.saveLog(null, order.getOrderId(), order.getOrderNo(), order.getOrderAid(), "推送订单物流", order.getTrackInfo(), null, System.currentTimeMillis());

        R<?> r = pushDo(order);
        orderLogService.updateLog(log.getLogId(), null, JSONObject.toJSONString(r, JSONWriter.Feature.WriteMapNullValue), System.currentTimeMillis() - log.getTime());

        //推送失败处理重推
        if (R.isError(r)) {
            //更新订单
            PayOrder up = new PayOrder();
            up.setOrderId(order.getOrderId());
            up.setTrackStatus(3);
            up.setTrackRemark(r.getMsg());
            orderService.updatePayOrderAsync(up);

//            trackSender.sendTrackMessage(param, 1000L);
        }
    }

    /**
     * 处理推送
     */
    public R<?> pushDo(PayOrder order) {
        try {
            //收款账号
            PayAccount account = accountService.selectById(order.getAccountId());
            if (account == null) {
                return R.fail("未查询到收款账号");
            }

            //获取账号请求地址
            String baseUrl = accountService.buildRequestUrl(account);
            if (StringUtils.isEmpty(baseUrl)) {
                return R.fail("未查询到收款账号请求地址");
            }

            String res;
            //构建参数
            JSONObject params = JSONObject.parseObject(order.getTrackInfo());
            try {
                //请求地址
                String url = baseUrl + "tracking";
                if (StringUtils.isNotEmpty(account.getPlugin())) {
                    url += "/";
                }
                //请求
                res = new RequestUtils().doPostJsonByToken(url, params.toJSONString());
            } catch (Exception e) {
                res = "【推送订单物流】请求失败：" + e.getMessage();
            }

            if(StringUtils.isEmpty(res)) {
                return R.fail("【推送订单物流】返回数据为空");
            }

            //解析返回参数
            R<?> r;
            try {
                r = JSONObject.parseObject(res, R.class);
            } catch (Exception e) {
                return R.fail(res);
            }

            //更新订单
            if (R.isSuccess(r)) {
                PayOrder up = new PayOrder();
                up.setOrderId(order.getOrderId());
                up.setTrackStatus(2);
                up.setTrackRemark("");
                orderService.updatePayOrderAsync(up);
            }

            return r;
        } catch (Exception e) {
            return R.fail("【推送订单物流】处理失败：" + e.getMessage());
        }
    }

    /**
     * 构建物流字段
     *
     * @param json      源数据
     * @param formItems 构建字段
     * @return 返回目标json
     */
    public JSONObject buildTrackField(JSONObject json, List<CustomField> formItems) {

        JSONObject track = new JSONObject();
        //自定义字段
        formItems.forEach(customField -> {
            String key = customField.getValue();
            Object value = json.get(key);
            if (value == null) {
                value = "";
            }
            track.put(key, value);
        });

        return track;
    }

    /**
     * 构建地址字段
     *
     * @param addressStr 订单默认地址信息
     * @param json       源数据
     * @param formItems  构建字段
     * @return 返回目标json
     */
    public JSONObject buildAddressField(String addressStr, JSONObject json, List<CustomField> formItems) {

        //默认地址信息
        AOrderBo.Address billing = JSONObject.parseObject(addressStr, AOrderBo.Address.class);
        JSONObject address = JSONObject.from(billing);

        //覆盖默认字段
        Field[] fields = billing.getClass().getDeclaredFields();
        for (Field field : fields) {
            String key = field.getName();
            Object value = json.get(key);
            if (value != null) {
                address.put(key, value);
            }
        }

        //自定义字段
        formItems.forEach(customField -> {
            String key = customField.getValue();
            Object value = json.get(key);
            if (value == null) {
                value = "";
            }
            address.put(key, value);
        });

        return address;
    }

    /**
     * 获取支付平台【物流字段配置】配置
     *
     * @param typeIds 平台ID
     * @return 配置
     */
    public Map<Long, LogisticsConfig> getLogisticsConfig(Set<Long> typeIds) {
        try {
            //获取支付平台配置
            List<PayTypeVo> payTypes = payTypeMapper.selectVoBatchIds(typeIds);
            if (payTypes == null || payTypes.isEmpty()) {
                return null;
            }

            Map<Long, LogisticsConfig> map = new HashMap<>();
            payTypes.forEach(type -> {
                JSONObject json = JSONObject.parseObject(StringUtils.removeComments(type.getConfig()));
                if (json == null) {
                    return;
                }
                map.put(type.getTypeId(), json.getObject("logisticsConfig", LogisticsConfig.class));
            });

            return map;
        } catch (Exception e) {
            log.error("获取支付平台【主动更新状态】配置 异常", e);
            return null;
        }
    }

}
